#include <iostream>

#include "CService.h"
#include "CServiceRepository.h"

#include "space/CSpace.h"
#include "space/CMessage.h"
#include "space/CKeyValue.h"

// user service
class CEchoService : public CService
{
public :
	CEchoService() : CService() {};
	CEchoService(char *name) : CService(name) {};
	CEchoService(const CEchoService &rhs) : CService(rhs) {};
	~CEchoService() {};

	CEchoService *clone() const { return new CEchoService(*this); };

	void run(cube__CObject *in, struct cube__invokeResponse &res) {
		res.out = in;
	};
};

// user service
class CSimpleDiscoveryService : public CService
{
public :
        CSimpleDiscoveryService() : CService() { };
        CSimpleDiscoveryService(char *name) : CService(name) { };
	//CSimpleDiscoveryService(const CSimpleDiscoveryService &rhs) { this->setName((char *)rhs.getName()); };
	CSimpleDiscoveryService(const CSimpleDiscoveryService &rhs) : CService(rhs) {};
        ~CSimpleDiscoveryService() {};

	CSimpleDiscoveryService *clone() const { return new CSimpleDiscoveryService(*this); };
	//CSimpleDiscoveryService &operator=(const CSimpleDiscoveryService &rhs) { this->setName((char *)rhs.getName()); return *this; };

	void run(cube__CObject *in, struct cube__invokeResponse &res) {
		CObjectArray *objs = (CObjectArray *)in;

#ifdef DEBUG
cout << "recieved messages = " << objs->__size << endl;
for(int i=0;i<objs->__size;i++) {
	cout << "[" << i << "]" << dynamic_cast<cube__CMessage *>(objs->__ptr[i])->get() << endl;
}
cout << endl;
#endif

		char *service_name = dynamic_cast<cube__CMessage *>(objs->__ptr[0])->get();
		bool all = (strcmp(service_name,"all")==0);

		vector< CSmartTemplate<CObject> > services;
		services = CServiceRepository::space->read(NULL, CServiceRepository::space->size(), services);

		CObjectArray *pobjs = (CObjectArray *)soap_new_CObjectArray(soap, -1);

		if(all) pobjs->__size = services.size();
		else pobjs->__size = 1; // find only one

		pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

		int count = 0;
		for(int i=0;i<services.size();i++) {
			CService *service = dynamic_cast<CService *>(&(*(services[i])));
			if(service==NULL) continue;

			if(all || strcmp(service_name, service->getName())==0) {
				pobjs->__ptr[count] = (cube__CMessage *)soap_new_cube__CMessage(soap, -1);
				dynamic_cast<cube__CMessage *>(pobjs->__ptr[count])->set(service->getName());

				count++;

				if(!all) break; // find only one
			}
		}

		res.out = pobjs;


#ifdef DEBUG
cout << "sending messages = " << ((CObjectArray *)res.out)->__size << endl;
for(int i=0;i<((CObjectArray *)res.out)->__size;i++) {
	cube__CMessage *msg = dynamic_cast<cube__CMessage *>(((CObjectArray *)res.out)->__ptr[i]);

	cout << "[" << i << "]" << msg->get() << endl;
}
cout << endl;
#endif


        };

};


// space service
class CSimpleSpaceService : public CService
{
private:
	static int ref_count;
	CSpace *sample_space;

public:
	CSimpleSpaceService(char *name) : CService(name) { 
		sample_space = new CSpace("sample.user.space");

		ref_count++;
	};
	CSimpleSpaceService(const CSimpleSpaceService &rhs) : CService(rhs) { 
		//this->setName((char *)rhs.getName()); 
		sample_space = rhs.sample_space; 

		ref_count++;
	};
	~CSimpleSpaceService() { 
		ref_count--;

		//if(ref_count<=0) delete sample_space;
		if(ref_count<=1) delete sample_space;
	};

	CSimpleSpaceService *clone() const { 
		return new CSimpleSpaceService(*this); 
	};
        CSimpleSpaceService &operator=(const CSimpleSpaceService &rhs) { 
		this->setName((char *)rhs.getName()); 
		this->instance((char *)rhs.instance());

		sample_space = rhs.sample_space; 

		ref_count++;

		return *this; 
	};

	void run(cube__CObject *in, struct cube__invokeResponse &res) {
		CObjectArray *objs = (CObjectArray *)in;

#ifdef DEBUG
		cout << "recieved messages = " << objs->__size << endl;
		for(int i=0;i<objs->__size;i++) {
			cout << "[" << i << "]" << dynamic_cast<cube__CKeyValue *>(objs->__ptr[i])->getValue() << endl;
		}
		cout << endl;
#endif

		char *cmd = dynamic_cast<cube__CKeyValue *>(objs->__ptr[0])->getValue();

#ifdef DEBUG
		cout << "### command = " << cmd << endl;
#endif

		CObjectArray *pobjs = (CObjectArray *)soap_new_CObjectArray(soap, -1);

		// write
		if(objs->__size>0&&strcmp(cmd,"write")==0) {
			for(int i=1;i<objs->__size;i++) {
				CKeyValue kv;
				char *key = dynamic_cast<cube__CKeyValue *>(objs->__ptr[i])->getKey();
				char *value = dynamic_cast<cube__CKeyValue *>(objs->__ptr[i])->getValue();
				kv.set(key, value);

				sample_space->write(&kv);
			}

			pobjs->__size = 1;
			pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

			pobjs->__ptr[0] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);
			char rmsg[100];
			sprintf(rmsg, "%d", sample_space->size());
			dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[0])->set("total size", rmsg);
		}

		// read
		if(objs->__size>0&&strcmp(cmd, "read")==0) {
			int size = sample_space->size();

			pobjs->__size = size;
			pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

			for(int i=0;i<pobjs->__size;i++) {
				CKeyValue *kv= NULL;
				kv = dynamic_cast<CKeyValue *>(sample_space->readIfExists(kv));

				if(kv!=NULL) {
					pobjs->__ptr[i] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);
					dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[i])->set(kv->getKey(),kv->getValue());
				}
				else break;

				delete kv;
			}
		}



		// take
		if(objs->__size>0&&strcmp(cmd, "take")==0) {
			int size = sample_space->size();
	
			pobjs->__size = size;
			pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

			for(int i=0;i<size;i++) {
				CKeyValue *kv= NULL;
				kv = dynamic_cast<CKeyValue *>(sample_space->take(kv));

				pobjs->__ptr[i] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);
				dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[i])->set(kv->getKey(),kv->getValue());

				delete kv;
			}
		}

		res.out = pobjs;
	};
};



// sort service
class CSortService : public CService
{
public :
	CSortService() : CService() {};
	CSortService(char *name) : CService(name) {};
	CSortService(const CSortService &rhs) : CService(rhs) {};
	~CSortService() {};

	CSortService *clone() const { return new CSortService(*this); };

	void run(cube__CObject *in, struct cube__invokeResponse &res) {
		map<string,string> iparam;

		if(parameterize(in, iparam)<0) return;

		// sort & response it immediately
		incarnate(res, iparam);
	};
};


// static reference count for "sample.space.service"
int CSimpleSpaceService::ref_count = 0;


int main(int argc, char **argv)
{
	CServiceRepository *service_repository = NULL;

	// cgi
	if(argc<2) {
		service_repository = new CServiceRepository();
	}
	// stand-alone
	else {
		service_repository = new CServiceRepository(atoi(argv[1]));
	}

	CEchoService service1("sample.echo.service1");
	CEchoService service2("sample.echo.service2");
	CSimpleDiscoveryService service3("sample.discovery.service1");
	CSimpleDiscoveryService service4("sample.discovery.service2");
	CSimpleSpaceService service5("sample.space.service");
	CSortService service6("keyvalue.sort.service");

	service_repository->plugin(&service1);
	service_repository->plugin(&service2);
	service_repository->plugin(&service3);
	service_repository->plugin(&service4);
	service_repository->plugin(&service5);
	service_repository->plugin(&service6);

	// cgi
	if(argc<2) service_repository->runCGI();
	// stand-alone
	else {
		char *name = NULL;;
		cout << "# registered services" << endl;
		cout << service1.getName() << endl;
		cout << service2.getName() << endl;
		cout << service3.getName() << endl;
		cout << service4.getName() << endl;
		cout << service5.getName() << endl;
		cout << service6.getName() << endl;

		service_repository->run();
	}
	
	delete service_repository;

        return 1;
}
